import { SortMenuDto } from './dto/sort.dto';
import { UpdateMenuDto, UpdateMenuEntity } from './dto/updateMenu.dto';
import { SysPermissionEntity } from 'src/entity/sys-permission.entity';
import { User } from '../../interfaces/request.interface';
import { map } from 'rxjs/operators';
import { SysStaffRoleEntity } from '../../entity/sys-staff-role.entity';
import { AuthService } from '../auth/auth.service';
import { LoginDto } from '../user/dto/login.dto';
import { CommonRepService } from '../../common/services/commonRep.service';
import { Injectable, BadRequestException, UseGuards, forwardRef, Inject, Param } from '@nestjs/common';
import { SysStaffEntity } from 'src/entity/sys-staff.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, MoreThan, MoreThanOrEqual, IsNull, Not } from 'typeorm';
import { CreateMenuDto } from './dto/createMenu.dto';

@Injectable()
export class MenuService extends CommonRepService<SysPermissionEntity, CreateMenuDto, UpdateMenuEntity>{
    constructor(
        @InjectRepository(SysPermissionEntity) private readonly permissionServ: Repository<SysPermissionEntity>,
    ) {
        super(permissionServ);
        this.defineGetChildLst();
    }


    async order(dto: SortMenuDto): Promise<any> {
        if (!dto.pId) dto.pId = null;
        const list = await this.permissionServ.find({ where: { pId: dto.pId, id: Not(dto.id), sort: MoreThanOrEqual(dto.sort) }, order: { sort: 'ASC' } });
        let sort: number = dto.sort;
        if (list[0]) {
            sort = list[0].sort + 1
        };
        return this.updateById(dto.id, { sort } as UpdateMenuEntity);
    }

    async addMenu(dto: CreateMenuDto): Promise<any> {
        dto.type = '1';
        const menu = await this.permissionServ.findOne({ path: dto.path });
        console.log("🚀 ~ file: menu.service.ts ~ line 26 ~ MenuService ~ addMenu ~ menu", menu)
        if (menu) {
            throw new BadRequestException(`添加失败，路由路径(${dto.path})已存在！`);
        }
        return this.createOne(dto);
    }

    async queryMenus(): Promise<any> {
        type Obj = {
            a: string;
            b: number;
            c: boolean;
        }
        type PickObj = Pick<Obj, 'a' | 'b'>
        var a: PickObj;
        type ParamType<T> = T extends (param: infer P) => any ? P : T;
        interface User {
            name: string;
            age: number;
        }
        type Func = (user: User) => void;
        type Param = ParamType<Func>;
        type A = ParamType<string>;

        // 查询所有父级菜单，及其权限
        const parents = await this.permissionServ.query(`
            SELECT sp.*,IFNULL(sp.pId,'') pId,IFNULL(GROUP_CONCAT(sr.name),'') roles
            FROM sys_permission sp 
            LEFT JOIN sys_role_permission srp on srp.premissionId = sp.id
            LEFT JOIN sys_role sr on sr.id = srp.roleId
            WHERE sp.pId IS NULL AND sp.type = 1
            GROUP BY sp.id
            ORDER by sp.sort DESC;
        `);
        this.findAllChildren(parents);
        return { list: parents };
    }


    async findAllChildren(parents) {
        for (const parent of parents) {
            let children = await this.getchildByPid(parent.id);
            parent['children'] = filterChild(children, children);
        }

        /**
         *  传入两个数组（同一份地址），通过两层for遍历 + 递归的方式 把一维数组转换成树形结构
         *   step1：传入两份同样的地址的数组是为了递归时始终能访问到母本数组
         *   step2： 两层for循环是根据pId找到自己的孩子 
         *   step3：如果此时有孩子，则对孩子进行递归处理
         */
        function filterChild(children: any[], childrenCopy: any[]) {
            for (let i = 0; i < children.length; i++) {
                const item = children[i];
                for (let j = 0; j < childrenCopy.length; j++) {
                    const element = childrenCopy[j];

                    if (item.id == element.pId) {
                        if (!item.children) {
                            item.children = [];
                        }
                        // 待思考 为什么这么写不行？
                        // item.children.push( [...childrenCopy.splice(j, 1)]);

                        item.children.push(element)
                        for (let z = 0; z < childrenCopy.length; z++) {
                            const one = childrenCopy[z];
                            if (one.id === element.id) {
                                childrenCopy.splice(z, 1)
                                j--;
                                break;
                            }
                        }
                    }
                }
                if (item.children) {
                    filterChild(item.children, childrenCopy)
                }
            }
            return children;
        }

    }

    /**
     *  根据父级id ，查询所有下级菜单
     */
    async getchildByPid(pId: string) {
        // GROUP_CONCAT(sr.name) roles
        return this.permissionServ.query(`
            SELECT sp.*,IFNULL(GROUP_CONCAT(sr.name),'') roles
            FROM sys_permission sp 
            LEFT JOIN sys_role_permission srp on srp.premissionId = sp.id
            LEFT JOIN sys_role sr on sr.id = srp.roleId
            WHERE find_in_set(sp.id, getChildLst(${pId})) and sp.id<>${pId} and sp.type=1
            GROUP BY sp.id
            ORDER by sp.sort DESC;
           
        `)
    }

    /**
     *  定义mysql查询下级菜单函数 
     */
    async defineGetChildLst() {
        try {
            await this.permissionServ.query(`
            CREATE FUNCTION \`getChildLst\`(rootId INT)
            
            RETURNS varchar(1000) READS SQL DATA
            
            BEGIN
            DECLARE sTemp VARCHAR(1000);
            
            DECLARE sTempChd VARCHAR(1000);
            
            SET sTemp = '$';
            
            SET sTempChd =cast(rootId as CHAR);
            
            WHILE sTempChd is not null DO
            
                SET sTemp = concat(sTemp,',',sTempChd);
                
                SELECT group_concat(id) INTO sTempChd FROM sys_permission where FIND_IN_SET(pId,sTempChd)>0;
            
            END WHILE;
            
            RETURN sTemp;
            
            END;
        `)
        } catch (error) {

        }

    }
}
